// File: jmcomic-core/src/main/java/dev/jukomu/jmcomic/api/JmcomicApi.java
package dev.jukomu.jmcomic.api;

import dev.jukomu.common.base.concurrent.MultiTaskExecutor;
import dev.jukomu.common.base.packer.PackerUtils;
import dev.jukomu.common.util.Asserts;
import dev.jukomu.jmcomic.config.JmModuleConfig;
import dev.jukomu.jmcomic.downloader.JmDownloader;
import dev.jukomu.jmcomic.entity.JmAlbumDetail;
import dev.jukomu.jmcomic.entity.JmPhotoDetail;
import dev.jukomu.jmcomic.option.JmOption;
import dev.jukomu.jmcomic.util.JmcomicText;

import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * 对应Python的api.py，提供给用户的顶层API。
 * 包含批量下载、创建Option等便捷方法。
 */
public final class JmcomicApi {

    /**
     * 批量下载本子或章节。
     * 每个ID对应一个独立的下载线程和JmDownloader实例。
     *
     * @param downloadFunction 下载函数（例如JmcomicApi::downloadAlbum或JmcomicApi::downloadPhoto）。
     * @param jmIdIter         禁漫ID的迭代器或集合。
     * @param option           下载选项，所有ID共享同一个Option实例。
     * @param <T>              下载函数返回的详情实体类型（JmAlbumDetail或JmPhotoDetail）。
     * @return 一组包含下载结果（详情实体和下载器）的Map.Entry。
     */
    public static <T> Set<Map.Entry<T, JmDownloader>> downloadBatch(
            Function<String, Map.Entry<T, JmDownloader>> downloadFunction,
            Iterable<String> jmIdIter,
            JmOption option) {

        JmOption finalOption = (option != null) ? option : JmOption.defaultOption();

        // 使用线程安全的Set来收集结果
        Set<Map.Entry<T, JmDownloader>> results = Collections.newSetFromMap(new ConcurrentHashMap<>());

        // 将ID去重
        Set<String> uniqueJmIds = StreamSupport.stream(jmIdIter.spliterator(), false)
                .map(JmcomicText::parseToJmId)
                .collect(Collectors.toSet());

        // 使用MultiTaskExecutor并发执行下载
        try {
            MultiTaskExecutor.launch(
                    uniqueJmIds,
                    jmId -> {
                        Map.Entry<T, JmDownloader> result = downloadFunction.apply(jmId);
                        results.add(result);
                    },
                    true // 等待所有任务完成
            );
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            JmModuleConfig.jmLog("api.batch", "批量下载任务被中断：" + e.getMessage());
        }

        return results;
    }

    /**
     * 下载一个或多个本子。
     *
     * @param jmAlbumId       单个本子ID（String）或本子ID集合（Iterable<String>）。
     * @param option          下载选项。
     * @param downloaderClass 下载器类（可选，如果为null则使用默认下载器）。
     * @param callback        回调函数，接收下载完成的本子详情和下载器实例（可选）。
     * @param checkException  是否在下载结束后检查并抛出部分下载失败的异常。
     * @return 如果是单个ID，返回一个包含本子详情和下载器的Map.Entry；如果是多个ID，返回一个Set。
     */
    @SuppressWarnings("unchecked")
    public static Object downloadAlbum(Object jmAlbumId, JmOption option, Class<? extends JmDownloader> downloaderClass, BiConsumer<JmAlbumDetail, JmDownloader> callback, boolean checkException) {
        if (jmAlbumId instanceof Iterable) {
            // 批量下载
            return downloadBatch(
                    id -> (Map.Entry<JmAlbumDetail, JmDownloader>) downloadAlbum(id, option, downloaderClass, callback, checkException),
                    (Iterable<String>) jmAlbumId,
                    option
            );
        }

        // 单个下载
        String albumIdStr = JmcomicText.parseToJmId(String.valueOf(jmAlbumId));
        JmDownloader downloader = newDownloader(option, downloaderClass);
        try (JmDownloader dler = downloader) { // 使用try-with-resources确保资源处理
            JmAlbumDetail album = dler.downloadAlbum(albumIdStr);

            if (callback != null) {
                callback.accept(album, dler);
            }
            if (checkException) {
                dler.raiseIfHasException();
            }
            return new SimpleEntry<>(album, dler);
        }
    }

    // 提供便捷的重载方法
    public static Object downloadAlbum(Object jmAlbumId, JmOption option) {
        return downloadAlbum(jmAlbumId, option, null, null, true);
    }

    public static Object downloadAlbum(Object jmAlbumId) {
        return downloadAlbum(jmAlbumId, null, null, null, true);
    }

    /**
     * 下载一个或多个章节。
     *
     * @param jmPhotoId       单个章节ID（String）或章节ID集合（Iterable<String>）。
     * @param option          下载选项。
     * @param downloaderClass 下载器类（可选，如果为null则使用默认下载器）。
     * @param callback        回调函数，接收下载完成的章节详情和下载器实例（可选）。
     * @param checkException  是否在下载结束后检查并抛出部分下载失败的异常。
     * @return 如果是单个ID，返回一个包含章节详情和下载器的Map.Entry；如果是多个ID，返回一个Set。
     */
    @SuppressWarnings("unchecked")
    public static Object downloadPhoto(Object jmPhotoId, JmOption option, Class<? extends JmDownloader> downloaderClass, BiConsumer<JmPhotoDetail, JmDownloader> callback, boolean checkException) {
        if (jmPhotoId instanceof Iterable) {
            // 批量下载
            return downloadBatch(
                    id -> (Map.Entry<JmPhotoDetail, JmDownloader>) downloadPhoto(id, option, downloaderClass, callback, checkException),
                    (Iterable<String>) jmPhotoId,
                    option
            );
        }

        // 单个下载
        String photoIdStr = JmcomicText.parseToJmId(String.valueOf(jmPhotoId));
        JmDownloader downloader = newDownloader(option, downloaderClass);
        try (JmDownloader dler = downloader) { // 使用try-with-resources
            JmPhotoDetail photo = dler.downloadPhoto(photoIdStr);

            if (callback != null) {
                callback.accept(photo, dler);
            }
            if (checkException) {
                dler.raiseIfHasException();
            }
            return new SimpleEntry<>(photo, dler);
        }
    }

    // 提供便捷的重载方法
    public static Object downloadPhoto(Object jmPhotoId, JmOption option) {
        return downloadPhoto(jmPhotoId, option, null, null, true);
    }

    public static Object downloadPhoto(Object jmPhotoId) {
        return downloadPhoto(jmPhotoId, null, null, null, true);
    }

    /**
     * 创建一个新的下载器实例。
     *
     * @param option          下载选项。
     * @param downloaderClass 下载器类。
     * @return JmDownloader实例。
     */
    public static JmDownloader newDownloader(JmOption option, Class<? extends JmDownloader> downloaderClass) {
        JmOption finalOption = (option != null) ? option : JmOption.defaultOption();

        Class<?> dlerClass = (downloaderClass != null) ? downloaderClass : JmModuleConfig.getDownloaderClass();
        if (dlerClass == null) {
            // 假设默认下载器是JmDownloader
            dlerClass = JmDownloader.class;
        }

        try {
            // 假设下载器有一个接受JmOption的构造函数
            return (JmDownloader) dlerClass.getConstructor(JmOption.class).newInstance(finalOption);
        } catch (Exception e) {
            throw new RuntimeException("Failed to create JmDownloader instance", e);
        }
    }

    /**
     * 根据文件路径创建JmOption实例。
     *
     * @param filepath 配置文件路径。
     * @return JmOption实例。
     */
    public static JmOption createOption(String filepath) {
        return JmOption.fromFile(filepath);
    }

    /**
     * 根据环境变量创建JmOption实例。
     *
     * @param envName 环境变量名，默认为 "JM_OPTION_PATH"。
     * @return JmOption实例。
     * @throws AssertionError 如果环境变量未设置。
     */
    public static JmOption createOptionByEnv(String envName) {
        String filepath = System.getenv(envName);
        Asserts.requireTrue(filepath != null && !filepath.isEmpty(), "未配置环境变量: " + envName + "，请配置为option的文件路径");
        return createOption(filepath);
    }

    /**
     * 根据环境变量创建JmOption实例，使用默认环境变量名 "JM_OPTION_PATH"。
     *
     * @return JmOption实例。
     */
    public static JmOption createOptionByEnv() {
        return createOptionByEnv("JM_OPTION_PATH");
    }

    /**
     * 根据YAML或JSON字符串创建JmOption实例。
     *
     * @param text 配置字符串。
     * @param mode 配置格式（"yml"或"json"）。
     * @return JmOption实例。
     */
    public static JmOption createOptionByStr(String text, String mode) {
        if (mode == null) {
            mode = PackerUtils.MODE_YML; // 默认使用YAML
        }
        try {
            Map<String, Object> data = (Map<String, Object>) PackerUtils.unpackByString(text, mode);
            return JmOption.construct(data, true);
        } catch (IOException e) {
            throw new RuntimeException("Failed to create JmOption from string", e);
        }
    }

    // 私有构造函数，防止实例化
    private JmcomicApi() {
    }
}